package org.project.d.network.protocol;

import io.netty.buffer.ByteBuf;
import io.netty.channel.socket.DatagramPacket;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.project.d.network.Config;

/**
 * @author lin
 * Email 451846939@qq.com
 * @version V1.0
 * @description
 * @date 2020/3/16 11:32 下午
 */
@Slf4j
@Data
//+rpcNum(1)+cmd(2)+flags(1)+serializationType(1)+lrc(1)+bodyLength(4)+body(n)
public class Packet {
//    8+16+8+8+8+32
//    public static int headLenth = 80;
//    public static int lengthFieldOffset= 48;
    public static int headLenth = 10;
    public static int lengthFieldOffset= 6;
    public static int lengthFieldLength= 4;
    public static int packetMaxLength=Integer.MAX_VALUE;
    /**
     * 协议号 8
     */
    private byte rpcNum;
    /**
     * cmd 16
     */
    private short cmd;
    /**
     * 特性，如是否加密，是否压缩等 8
     */
    private byte flags;
    /**
     * 序列化方式 8
     */
    private byte serializationType;
    /**
     * 校验 8
     */
    private byte lrc;
    /**
     * body数据长度 32
     */
    private int bodyLength;

    private byte[] body;

    public Packet() {
        this.body=new byte[]{};
    }
    public Packet(byte serializationType,byte[] body) {
        this.serializationType=serializationType;
        this.body=body;

    }
    public Packet(Packet packet, short cmd, int bodyLength, byte[] body) {
        this.rpcNum=packet.getRpcNum();
        this.flags=packet.getFlags();
        this.serializationType=packet.getSerializationType();
        this.lrc=packet.getLrc();
        this.cmd = cmd;
        this.bodyLength = bodyLength;
        this.body = body;
    }
    public Packet(Packet packet) {
        this.rpcNum=packet.getRpcNum();
        this.flags=packet.getFlags();
        this.serializationType=packet.getSerializationType();
        this.lrc=packet.getLrc();
        this.cmd = packet.cmd;
        this.bodyLength = packet.bodyLength;
        this.body = packet.body;
    }
    public Packet(byte rpcNum, short cmd, byte flags, byte serializationType, byte lrc, int bodyLength, byte[] body) {
        this.rpcNum = rpcNum;
        this.cmd = cmd;
        this.flags = flags;
        this.serializationType = serializationType;
        this.lrc = lrc;
        this.bodyLength = bodyLength;
        this.body = body;
    }
    public Packet(byte rpcNum, byte flags, byte serializationType, byte lrc) {
        this.rpcNum = rpcNum;
        this.flags = flags;
        this.serializationType = serializationType;
        this.lrc = lrc;
    }
    public static Packet defultPacket(){
        return new Packet(Config.rpcNum, Config.flags,Config.serializationType,Config.lrc);
    }
    public static Packet decodePacket(ByteBuf in){
        Packet packet = new Packet();
        return decode(in, packet);
    }

    private static Packet decode(ByteBuf in, Packet packet) {
        packet.setRpcNum(in.readByte());
        packet.setCmd(in.readShort());
        packet.setFlags(in.readByte());
        packet.setSerializationType(in.readByte());
        packet.setLrc(in.readByte());
        packet.setBodyLength(in.readInt());
        if (in.writerIndex()<packet.getBodyLength()){
            log.warn("byteBuf.writerIndex < packet.getBodyLength() byteBuf.writerIndex:{}, Packet.headLenth:{} ",in.writerIndex(),packet.getBodyLength());
            return null;
        }
        if (packet.getBodyLength() > 0) {
            if (in.writerIndex()-in.readerIndex()<packet.getBodyLength()){
                log.warn("byteBuf.writerIndex-readerIndex < packet.getBodyLength() byteBuf.writerIndex-readerIndex:{}, Packet.headLenth:{} ",in.writerIndex()-in.readerIndex(),packet.getBodyLength());
                return packet;
            }
            byte[] bytes = new byte[packet.getBodyLength()];
            in.readBytes(bytes);
            packet.setBody(bytes);
        }
        return packet;
    }

    public static Packet decodePacket(DatagramPacket datagramPacket){
        ByteBuf in = datagramPacket.content();
        if (in.writerIndex()<Packet.headLenth){
            log.warn("datagramPacket.content < Packet.headLenth datagramPacket:{}, Packet.headLenth:{} ",datagramPacket.content(),Packet.headLenth);
            return null;
        }
        UDPPacket packet = new UDPPacket();
        UDPPacket decode = (UDPPacket) decode(in, packet);
        if (decode!=null){
            decode.setAddress(datagramPacket.sender());
        }
        return decode;
    }

    public static void encodePacket(Packet packet, ByteBuf out){
        out.writeByte(packet.getRpcNum());
        out.writeShort(packet.getCmd());
        out.writeByte(packet.getFlags());
        out.writeByte(packet.getSerializationType());
        out.writeByte(packet.getLrc());
        out.writeInt(packet.getBodyLength());
        if (packet.getBodyLength()>0){
            out.writeBytes(packet.getBody());
        }
    }
}
